package com.amtf.datafile3.core.excel;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;

import com.amtf.datafile3.DataException;
import com.amtf.datafile3.DataField;
import com.amtf.datafile3.DataFieldDefinition;
import com.amtf.datafile3.DataRecord;
import com.amtf.datafile3.DataTable;
import com.amtf.datafile3.DataTableDefinition;
import com.amtf.datafile3.cfg.excel.ExcelDataTableDefinition;
import com.amtf.datafile3.core.excel.parser.AbsExcelDataFieldParser;
import com.amtf.datafile3.core.excel.parser.ExcelDataRecordParser;
import com.amtf.datafile3.core.std.AbsDataTable;
import com.amtf.datafile3.core.std.type.DateField;
import com.amtf.datafile3.core.std.type.DecimalField;
import com.amtf.datafile3.core.std.type.StringField;

public class ExcelDataTable extends AbsDataTable<Row, Cell> {
	private Sheet workSheet = null;

	private int startReadRow = 0;
	private int startReadColumn = 0;
	// private int maxReadRow = 1048576;// 65535;
	// private int maxReadColumn = 16384;// 256;
	private int startWriteRow = 0;
	private int startWriteColumn = 0;

	private boolean firstRowTitle;

	public ExcelDataTable(String name, DataTableDefinition definition, Sheet workSheet, FormulaEvaluator evaluator) throws DataException {
		super(name, definition);
		this.workSheet = workSheet;
		this.definition = definition;

		// if (definition.getFieldCount() != 0) {
		// this.maxReadColumn = definition.getFieldCount();
		// }

		ExcelDataTableDefinition exceldt = (ExcelDataTableDefinition) definition;
		this.firstRowTitle = exceldt.isFirstRowTitle();

		this.startReadRow = exceldt.getFirstReadingRow();
		this.startReadColumn = exceldt.getFirstReadingColumn();
		// this.maxReadRow = maxReadRow;
		// this.maxReadColumn = maxReadColumn;
		this.startWriteRow = exceldt.getFirstWritenRow();
		this.startWriteColumn = exceldt.getFirstWritenColumn();

		exceldt.setEvaluator(evaluator);

		readTitleLine();
	}

	/**
	 * @param startRow
	 *            1,2,3,....
	 * @param startColumn
	 *            1,2,3,....
	 * @param maxRow
	 *            1,2,3,....
	 * @param maxColumn
	 */
	public void setReadingRange(int startRow, int startColumn) {
		if (startRow <= 1) {
			startRow = 0;
		} else {
			startRow -= 1;
		}
		if (startColumn <= 1) {
			startColumn = 0;
		} else {
			startColumn -= 1;
		}
		// if (maxColumn > 16384 || maxColumn <= 0) {
		// maxColumn = 16384 - 1;
		// }
		// if (maxRow > 1048576 || maxRow <= 0) {
		// maxRow = 1048576 - 1;
		// }
		// if (maxColumn <= 0) {
		// maxColumn = -1;
		// }
		// if (maxRow <= 0) {
		// maxRow = 1;
		// }

		this.startReadRow = startRow;
		this.startReadColumn = startColumn;
		// this.maxReadRow = maxRow + startReadRow;
		// this.maxReadColumn = maxColumn;

		this.currentReadingLine = startReadRow;
	}

	public void setWritenRange(int startRow, int startColumn) {
		if (startRow < 0) {
			startRow = 0;
		}
		if (startColumn < 0) {
			startColumn = 0;
		}

		this.startWriteRow = startRow;
		this.startWriteColumn = startColumn;

		this.currentWritenLine = startWriteRow;
	}

	@Override
	public void setDefinition(DataTableDefinition definition) {
		super.setDefinition(definition);
		// this.maxReadColumn = definition.getFieldCount();

		FormulaEvaluator evaluator = workSheet.getWorkbook().getCreationHelper().createFormulaEvaluator();
		((ExcelDataRecordParser) definition.getParser()).setEvaluator(evaluator);
		List<DataFieldDefinition> fields = definition.getFields();
		for (DataFieldDefinition field : fields) {
			((AbsExcelDataFieldParser) field.getParser()).setEvaluator(evaluator);
		}
	}

	private void readTitleLine() throws DataException {
		Row row = null;
		if (firstRowTitle) {
			row = this.workSheet.getRow(startReadRow++);
		} else {
			row = this.workSheet.getRow(startReadRow + 1);
		}
		recordParser.detectColumn(definition, row, firstRowTitle);
	}

	public DataRecord readFirstLine() throws DataException {
		this.currentReadingLine = startReadRow;

		DataRecord record = readNextLine();

		return record;
	}

	public DataRecord readNextLine() throws DataException {
		// if (this.currentReadingLine >= this.maxReadRow) {
		// return null;
		// }

		Row row = this.workSheet.getRow(this.currentReadingLine++);
		if (row != null) {
			return recordParser.parse(definition, row);
		}

		return null;
	}

	public List<DataRecord> readFirstLines(int readCount) throws DataException {
		this.currentReadingLine = startReadRow;

		List<DataRecord> records = readNextLines(readCount);

		return records;
	}

	public List<DataRecord> readNextLines(int readCount) throws DataException {
		Row row = null;
		List<DataRecord> records = new ArrayList<DataRecord>(readCount);
		for (int i = 0; i < readCount; i++) {
			// if (this.currentReadingLine >= this.maxReadRow) {
			// break;
			// }

			row = this.workSheet.getRow(this.currentReadingLine++);
			if (row != null) {
				records.add(recordParser.parse(definition, row));
			} else {
				break;
			}

			// if (maxReadColumn <= 0) {
			// maxReadColumn = definition.getFieldCount();
			// }
		}
		return records;
	}

	public void writeTitle(String[] titleName) throws DataException {
		Row row = this.workSheet.getRow(this.currentWritenLine);
		if (row == null) {
			row = this.workSheet.createRow(this.currentWritenLine);
		}

		for (int i = 0; i < titleName.length; i++) {
			Cell cell = row.getCell(i);
			if (cell == null) {
				cell = row.createCell(i);
			}

			cell.setCellValue(titleName[i]);
		}

		this.currentWritenLine++;
	}

	public void writeRecord(DataRecord line) throws DataException {
		Row row = this.workSheet.getRow(this.currentWritenLine);
		if (row == null) {
			row = this.workSheet.createRow(this.currentWritenLine);
		}

		DataField[] fields = line.getFields();
		for (int i = this.startWriteColumn; i < this.startWriteColumn + fields.length; i++) {
			DataField dataField = fields[i];

			Cell cell = row.getCell(i);
			if (cell == null) {
				cell = row.createCell(i);
			}

			if (dataField instanceof StringField) {
				cell.setCellValue(((StringField) dataField).getValue());
			} else if (dataField instanceof DecimalField) {
				BigDecimal v = ((DecimalField) dataField).getValue();
				if (v != null) {
					cell.setCellValue(v.doubleValue());
				}
			} else if (dataField instanceof DateField) {
				cell.setCellValue(((DateField) dataField).getValue());
			} else {
				cell.setCellValue(dataField.toString());
			}
		}

		this.currentWritenLine++;
	}

	public void writeRecord(List<DataRecord> lines) throws DataException {
		for (DataRecord line : lines) {
			writeRecord(line);
		}
	}

	public void writeAll(DataTable tableSet) throws DataException {

		List<DataRecord> lines = tableSet.readFirstLines(500);
		if (lines != null && lines.size() > 0) {
			if (firstRowTitle) {
				writeTitle(definition.getFieldNames());
			}

			do {
				this.writeRecord(lines);
				lines = tableSet.readNextLines(500);
			} while (lines != null && lines.size() > 0);
		}
	}

	// public void writeRecord(String[] line) throws DataException {
	// writeRecord(recordParser.parse(definition, line));
	// }
	//
	// public void writeRecord(Object[] line) throws DataException {
	// writeRecord(recordParser.parse(definition, line));
	// }

	public void autoSizeColumn(int fromColumn, int toColumn) {
		for (int i = fromColumn; i < toColumn; i++) {
			this.workSheet.autoSizeColumn(i);
		}
	}

	public String getName() {
		return workSheet.getSheetName();
	}

	public void setName(String name) {
		int isht = workSheet.getWorkbook().getSheetIndex(workSheet);
		workSheet.getWorkbook().setSheetName(isht, name);
	}

	public void close() {
	}

	public void truncate() throws DataException {
		// workSheet.
	}

}
